#include "stdio.h"	//I/O函数
#include "stdlib.h" //标准库函数
#include "string.h" //字符串函数
#include "ctype.h"	//字符操作函数
#include "conio.h"	//控制台输入输出函数
#include "malloc.h" //动态地址分配函数
#include "memory.h" //内存操作函数
#define N 3			// 定义课程门数，可以根据情况设定

typedef struct shopping // 定义数据结构
{
	char no[10];		   // 商品号
	char name[20];		   // 商品名
	float price;		   // 商品价格
	char date[12];		   // 生产日期
	struct shopping *next; // 指向后继结点的指针
} SHO;					   // 结构体类型名

/**********以下是函数原型***********/
SHO *init();								   // 初始化函数
SHO *create();								   // 创建链表
void print(SHO *h);							   // 显示所有记录
void search(SHO *h);						   // 查找记录
SHO *insert(SHO *h);						   // 插入记录
SHO *modify(SHO *h);						   // 修改记录
SHO *delet(SHO *h);							   // 删除记录
void save(SHO *h);							   // 记录保存为文件
SHO *load();								   // 从文件中读记录
void copy();								   // 文件备份
void inputs(char *prompt, char *s, int count); // 输入字符串并进行长度验证
int menu_select();							   // 主菜单函数
//////
int main()
{

	SHO *head;	   // 链表定义头指针
	head = init(); // 初始化链表
	system("cls"); // 清屏
	for (;;)	   // 无限循环
	{
		switch (menu_select())
		{
		case 0:
			free(head); // 释放内存
			head = init();
			break; // 执行初始化
		case 1:
			free(head); // 释放内存
			head = create();
			break; // 输入记录创建链表
		case 2:
			print(head);
			break; // 显示所有记录
		case 3:
			search(head);
			break; // 查找记录
		case 4:
			head = insert(head);
			break; // 插入记录
		case 5:
			head = modify(head);
			break; // 修改记录
		case 6:
			head = delet(head);
			break; // 删除记录
		case 7:
			save(head);
			break; // 保存文件
		case 8:
			head = load();
			break; // 读文件
		case 9:
			copy();
			break; // 文件备份
		case 10:
			free(head); // 释放内存
			exit(0);	// 结束程序
		}
		//	free(head); // 释放内存
	}

	return 0;
}
int menu_select()
{
	char s[80];
	int c;
	printf("按任意键继续\n");
	getch();
	system("cls"); // 清屏
	printf("*************购物管理系统菜单***************\n\n");
	printf("            0.初始化\n");
	printf("            1.输入记录\n");
	printf("            2.显示全部记录\n");
	printf("            3.查找记录\n");
	printf("            4.插入记录\n");
	printf("            5.修改记录\n");
	printf("            6.删除记录\n");
	printf("            7.保存文件\n");
	printf("            8.读文件\n");
	printf("            9.文件备份\n");
	printf("            10.退出\n");
	printf("************************************************\n");
	do
	{
		printf("\n 输入你的选择(0-10):");
		scanf("%s", s);
		c = atoi(s); // 将数字字符串转换为数值
	} while (c < 0 || c > 10);
	return c;
}

/*初始化链表*/
SHO *init()
{
	return NULL;
}

/*输入字符串，并进行长度验证*/
void inputs(char *prompt, char *s, int count)
{
	char p[255];
	do
	{
		printf(prompt); // 显示提示信息
		scanf("%s", p); // 输入字符串
		if (strlen(p) > count)
			printf("\ntoo long!\n"); // 进行长度验证，超过count值重新输入
	} while (strlen(p) > count);
	;
	strcpy(s, p); // 将输入的字符串拷贝到字符串s中
}

#define A 1;
/*创建链表*/
SHO *create()
{
#if 0
#if 1
	float s;
	SHO *h = NULL, *info, *tail;
	// SHO *info,*tail;
	h = (SHO *)malloc(sizeof(SHO));
	// tail=h;
	h->next = NULL;
	for (;;)
	{
		info = (SHO *)malloc(sizeof(SHO)); // 申请空间
		if (!info)						   // 若指针info为空
		{
			printf("\n out of memory"); // 提示内存溢出
			return NULL;				// 返回空指针
		}

		inputs("输入商品号：", info->no, 10); // 输入商品号并检验
		if (info->no[0] == '#')
			break;								// 如果学号首字符为#，结束输入
		inputs("输入商品名：", info->name, 20); // 输入商品名并检验
		printf("输入商品价格：");
		scanf("%f", &s);
		info->price = s;
		inputs("输入商品生产日期：", info->date, 12);
		printf("跳出就按 # 符号\n");
		info->next = NULL;
		info->next = h->next;
		h->next = info;

		//  info->next = NULL;
		//  tail->next=info;
		//  tail=info;
		// return h;//返回头指针
	}
	return h; // 返回头指针
#else
	float s;
	SHO *h = NULL, *info, *tail;
	// SHO *info,*tail;
	//  h=(SHO *)malloc(sizeof(SHO));
	//  tail=h;
	// h->next = NULL;
	h = NULL;
	for (;;)
	{
		info = (SHO *)malloc(sizeof(SHO)); // 申请空间
		if (!info)						   // 若指针info为空
		{
			printf("\n out of memory"); // 提示内存溢出
			return NULL;				// 返回空指针
		}

		inputs("输入商品号：", info->no, 10); // 输入商品号并检验
		if (info->no[0] == '#')
			break;								// 如果学号首字符为#，结束输入
		inputs("输入商品名：", info->name, 20); // 输入商品名并检验
		printf("输入商品价格：");
		scanf("%f", &s);
		info->price = s;
		inputs("输入商品生产日期：", info->date, 12);
		printf("跳出就按 # 符号\n");
		// info->next=NULL;
		//  info->next=h;
		//  h=info;

		info->next = h;
		h = info;
		// return h;//返回头指针
	}
	return h; // 返回头指针
#endif

#else

#if 0
float s;
	SHO *h = NULL, *info, *tail;
	// SHO *info,*tail;
	h = (SHO *)malloc(sizeof(SHO));
	tail = h;
	// h->next = NULL;
	for (;;)
	{
		info = (SHO *)malloc(sizeof(SHO)); // 申请空间
		if (!info)						   // 若指针info为空
		{
			printf("\n out of memory"); // 提示内存溢出
			return NULL;				// 返回空指针
		}

		inputs("输入商品号：", info->no, 10); // 输入商品号并检验
		if (info->no[0] == '#')
			break;								// 如果学号首字符为#，结束输入
		inputs("输入商品名：", info->name, 20); // 输入商品名并检验
		printf("输入商品价格：");
		scanf("%f", &s);
		info->price = s;
		inputs("输入商品生产日期：", info->date, 12);
		printf("跳出就按 # 符号\n");
		// info->next=NULL;
		//  info->next=h;
		//  h=info;

		info->next = NULL;
		tail->next = info;
		tail = info;
		// return h;//返回头指针
	}
	return h; // 返回头指针
#else
	float s;
	SHO *h = NULL, *info, *tail;
	// SHO *info,*tail;
	// h = (SHO *)malloc(sizeof(SHO));
	// tail = h;
	// h->next = NULL;
	tail = NULL;
	for (;;)
	{
		info = (SHO *)malloc(sizeof(SHO)); // 申请空间
		if (!info)						   // 若指针info为空
		{
			printf("\n out of memory"); // 提示内存溢出
			return NULL;				// 返回空指针
		}

		inputs("输入商品号：", info->no, 10); // 输入商品号并检验
		if (info->no[0] == '#')
			break;								// 如果学号首字符为#，结束输入
		inputs("输入商品名：", info->name, 20); // 输入商品名并检验
		printf("输入商品价格：");
		scanf("%f", &s);
		info->price = s;
		inputs("输入商品生产日期：", info->date, 12);
		printf("跳出就按 # 符号\n");
		info->next = NULL;
		//  info->next=h;
		//  h=info;
		if (h == NULL)
		{
			h = info;
			tail = info;
		}
		else
		{
			tail->next = info;
			tail = info;
		}

		// return h;//返回头指针
	}
	return h; // 返回头指针
#endif

#endif
}

/*输出全部记录*/
void print(SHO *h)
{
	int i = 0; // 统计记录个数
	SHO *p;	   // 移动指针
	system("cls");
	; // 清屏
	p = h;
	printf("\n\n\n************************购物管理信息*************************************\n");
	printf("商品号\t\t商品名\t\t商品价格\t生产日期\t\n");
	printf("-------------------------------------------------------------------------------\n");
	while (p != NULL)
	{
		i++;
		printf("  %s\t\t  %s\t\t  %.1f\t\t  %s\t\n", p->no, p->name, p->price, p->date);
		p = p->next;
	}
	printf("-------------------------------------------------------------------------------\n");
}

/*查找记录*/
void search(SHO *h)
{
	SHO *p;		   // 移动指针
	char s[20];	   // 存放商品名的字符数组
	system("cls"); // 清屏
	printf("请输入要查找的商品名：\n");
	scanf("%s", s); // 输入商品名
	p = h;
	while (strcmp(p->name, s))
	{
		p = p->next; // 移动指针，指向下一结点1
		if (p == NULL)
			break;
	}
	if (p == NULL)
		printf("没有商品名为%s的商品!\n", s);
	else
	{
		printf("\n\n\n***************************查找结果*********************************\n");
		printf("商品号号\t商品名\t\t价格\t\t生产日期\t\n");
		printf("--------------------------------------------------------------------------\n");
		printf("   %s\t\t  %s\t\t%.1f\t\t  %s\t\n", p->no, p->name, p->price, p->date);
		printf("--------------------------------------------------------------------------\n");
	}
}

/*插入记录*/
SHO *insert(SHO *h)
{
#if 0
	SHO *p, *q, *info; // p指向插入位置，q是其前趋，info指新插入记录
	char s[10];		   // 保存插入点位置的商品号
	float s1, i;
	printf("在哪个商品号前插入？");
	;
	scanf("%s", s);
	; // 输入插入点商品号
	printf("\n请输入新的记录：\n");
	info = (SHO *)malloc(sizeof(SHO)); // 申请空间
	if (!info)
	{
		printf("\n out of memory"); // 提示内存溢出
		return NULL;				// 返回空指针
	}
	inputs("输入商品号：", info->no, 10);	// 输入商品号并检验
	inputs("输入商品名：", info->name, 20); // 输入商品名并检验
	printf("输入商品价格:");
	scanf("%f", &i);
	info->price = i;
	inputs("输入商品生产日期：", info->date, 12);
	info->next = NULL; // 设后继指针为空
	p = h;
	q = h;
	while (p != NULL && strcmp(p->no, s)) // 查找插入位置
	{
		q = p;
		p = p->next;
	}
	if (p == NULL)	  // 如果p指针为空，说明没有指定结点
		if (p == h)	  // 同时p等于h，说明链表为空
			h = info; // 新记录则为头结点
		else
			q->next = info; // p不为空，但p不等于h，将新结点插在表尾
	else if (p == h)
	{
		info->next = p; // 如果p等于h，则新结点插入在第一个结点之前
		h = info;		// 新结点为头结点
	}
	else
	{
		info->next = p; // 不是头结点，则是中间某个位置，新结点的后继为p
		q->next = info; // 新结点作为q的后继结点
	}
	printf("成功插入！");
	return h;
#else
	SHO *p, *info;
	printf("在哪个商品号后插入？");
	char s[20];
	scanf("%s", s);
	; // 输入插入点商品号
	p = h;
	while (p != NULL && strcmp(s, p->no) != 0)
	{
		p = p->next;
	}
	if (p == NULL)
	{
		printf("无该项\n");
	}
	else
	{
		info = (SHO *)malloc(sizeof(SHO)); // 申请空间
		if (!info)
		{
			printf("\n out of memory"); // 提示内存溢出
			return NULL;				// 返回空指针
		}
		inputs("输入商品号：", info->no, 10);	// 输入商品号并检验
		inputs("输入商品名：", info->name, 20); // 输入商品名并检验
		printf("输入商品价格:");
		float i = 0;
		scanf("%f", &i);
		info->price = i;
		inputs("输入商品生产日期：", info->date, 12);
		info->next = NULL; // 设后继指针为空
		if (p->next == NULL)
		{
			p->next = info;
		}
		else
		{
			info->next = p->next;
			p->next = info;
		}
	}
	return h;
#endif
}

/*修改记录*/
SHO *modify(SHO *h)
{
	SHO *p;		// p为查找到要修改的结点指针
	char s[10]; // 存放学号
	float s1, i;
	system("cls"); // 清屏
	printf("请输入要修改商品的商品号:");
	scanf("%s", s);
	p = h;
	while (p != NULL && strcmp(p->no, s)) // 查找
	{
		p = p->next;
	}
	if (p == NULL)
		printf("没有该商品！\n");
	else
	{
		printf("找到该商品！商品信息见下列\n");
		printf("商品号号\t商品名\t\t价格\t\t生产日期\t\n");
		printf("--------------------------------------------------------------------------\n");
		printf("   %s\t\t  %s\t\t%.1f\t\t  %s\t\n", p->no, p->name, p->price, p->date);
		printf("--------------------------------------------------------------------------\n");
#if 1
		printf("请输入修改信息：\n");
		// inputs("输入商品号：", p->no, 10);	 // 输入商品号并检验
		char no[20];
		printf("输入商品名：");
		getchar(); // 获取到上次输入的换行
		fgets(no, sizeof no, stdin);
		if (*no != '\n')
		{
			sscanf(no, "%s", p->no);
		}
		inputs("输入商品名：", p->name, 20); // 输入商品名并检验
		printf("请输入商品的价格：");
		scanf("%lf", &i);
		p->price = i;
		inputs("输入商品的生产日期：", p->date, 12);
#else

#endif
		printf("修改成功！\n");
	}
	return h;
}

/*删除记录*/
SHO *delet(SHO *h)
{
	SHO *p, *q; // p为查找到要删除的结点指针,q为其前趋指针
	char s[10]; // 存放商品号
	char ch;
	system("cls"); // 清屏
	printf("请输入要删除的商品号:");
	scanf("%s", s);
	q = p = h;
	while (p != NULL && strcmp(p->no, s)) // 查找
	{
		q = p;
		p = p->next;
	}
	if (p == NULL)
		printf("没有该商品！\n");
	else
	{
		printf("找到该商品！商品信息见下列\n");
		printf("商品号号\t商品名\t\t价格\t\t生产日期\t\n");
		printf("--------------------------------------------------------------------------\n");
		printf("   %s\t\t  %s\t\t%.1f\t\t  %s\t\n", p->no, p->name, p->price, p->date);
		printf("--------------------------------------------------------------------------\n");
		printf("确定要删除这条记录吗？(Y/N):\n");
		ch = getch(); // 此处一定要用getch函数输入,其它函数均不可以
		if (ch == 'Y' || ch == 'y')
		{
			if (p == h)		 // 若被删结点是头结点
				h = p->next; // 修改头指针指向下一条记录
			else
				q->next = p->next; // 不是头结点,将p的后继结点作为q的后继结点
			free(p);
			printf("删除成功！\n");
		}
	}
	return h;
}

/*保存数据到文件*/
void save(SHO *h)
{
	FILE *fp;		  // 定义指向文件的指针
	SHO *p;			  // 定义移动指针
	//char outfile[50]; // 文件名
	// printf("请输入文件名，例如：c:\\f1\\te.dat:\n"); // 提示文件名格式信息
	// scanf("%s", outfile);
	if ((fp = fopen("./2.txt", "a+")) == NULL)
	{
		printf("不能打开文件!\n");
		exit(1);
	}
	printf("\n保存文件\n");
	p = h;
	while (p != NULL)
	{
		// 确保写入格式正确，并添加换行符
		if (fprintf(fp, "%s %s %lf %s\n", p->no, p->name, p->price, p->date) < 0)
		{
			printf("写入文件失败。\n");
			fclose(fp);
			return; // 返回，避免关闭未成功写入的文件
		}
		p = p->next; // 移动到下一个节点
	}
	fclose(fp);
	printf("成功保存文件!\n");
}

/*从文件读数据*/
SHO *load()
{
	SHO *p, *q, *h = NULL;
	FILE *fp;
	char infile[50];
	printf("请输入文件名，例如：c:\\f1\\te.dat:\n"); // 提示文件名格式信息
	scanf("%s", infile);
	if ((fp = fopen(infile, "rb")) == NULL)
	{
		printf("不能打开文件!\n");
		// exit(1);
	}
	printf("\n读文件\n");
	p = (SHO *)malloc(sizeof(SHO));
	if (!p)
	{
		printf("内存溢出！\n");
		return h;
	}
	h = p;
	while (!feof(fp))
	{
		if (1 != fread(p, sizeof(SHO), 1, fp))
			break;
		p->next = (SHO *)malloc(sizeof(SHO));
		if (!p->next)
		{
			printf("内存溢出！\n");
			return h;
		}
		q = p;
		p = p->next;
	}
	q->next = NULL;
	;
	fclose(fp);
	printf("成功读取文件！\n");
	return h;
}

/*文件备份*/
void copy()
{
	char outfile[20], infile[20];		 // 目标文件名
	FILE *sfp, *tfp;					 // 源文件指针，目标文件指针
	SHO *p = (SHO *)malloc(sizeof(SHO)); // 申请空间
	system("cls");
	printf("请输入源文件名，例如：c:\\f1\\te.dat:\n");
	scanf("%s", infile);
	if ((sfp = fopen(infile, "rb")) == NULL) // 打开记录文件
	{
		printf("不能打开文件!\n");
		exit(0);
	}
	printf("输入目标文件名，如c:\\f1\\te.dat:\n");
	scanf("%s", outfile);
	if ((tfp = fopen(outfile, "wb")) == NULL) // 打开目标文件
	{
		printf("不能打开文件!\n");
		exit(0);
	}
	while (!feof(sfp)) // 读文件直到文件尾
	{
		if (1 != fread(p, sizeof(SHO), 1, sfp))
			break;						// 块读
		fwrite(p, sizeof(SHO), 1, tfp); // 块写
	}
	fclose(sfp);
	fclose(tfp);
	printf("复制成功！\n");
}